package com.ruoyi.cat.service.impl;
//

import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.cat.domain.CatChances;
import com.ruoyi.cat.domain.CatPrizeDraw;
import com.ruoyi.cat.service.DrawLotteryService;
import com.ruoyi.cat.service.ICatChancesService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
public class CatRaffleServiceImpl implements DrawLotteryService {

    private static final Logger log = LoggerFactory.getLogger(CatRaffleServiceImpl.class);

    private static final String REDIS_PREFIX = "raffle:";
    private static final String UNLOCK_PLAN_SUFFIX = ":unlockPlan";
    private static final String DRAW_COUNT_SUFFIX = ":drawCount";
    private static final String OBTAINED_CARDS_SUFFIX = ":obtained";
    private static final String PROBABILITY_SUFFIX = ":prob";

    // 稀有度分层，数字代表需要抽多少次才能解锁相应稀有度的卡片
    // 不再使用固定的稀有度级别，而是使用数据库中配置的quantity字段作为解锁门槛

    // 过期时间设置，30天
    private static final long EXPIRE_DAYS = 7;

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Autowired
    private ICatChancesService catChancesService;

    /**
     * 执行抽奖逻辑 - 增强版，确保Redis过期时仍能正确工作
     * @param activityId 活动ID
     * @param userId 用户ID
     * @param userDraws 用户已有的抽奖记录
     * @return 抽中的卡片
     */
    @Override
    public CatChances drawCard(Long activityId, Long userId, List<CatPrizeDraw> userDraws) {
        String baseKey = REDIS_PREFIX + activityId + ":" + userId;

        // 开始记录抽奖流程
        log.info("开始抽奖流程 [用户:{}, 活动:{}, 已抽次数:{}]", userId, activityId, userDraws.size());

        // 获取所有卡片
        CatChances catChance = new CatChances();
        catChance.setActivityId(activityId);
        List<CatChances> allCards = catChancesService.selectCatChancesList(catChance);

        if (allCards.isEmpty()) {
            log.error("抽奖配置异常，未找到可抽取的卡片 [用户:{}, 活动:{}]", userId, activityId);
            throw new RuntimeException("抽奖配置异常，未找到可抽取的卡片");
        }

        // 创建卡片ID到卡片对象的映射
        Map<Long, CatChances> cardMap = allCards.stream()
                .collect(Collectors.toMap(CatChances::getId, card -> card));

        // 1. 获取或初始化解锁计划 - 支持Redis过期重建
        Map<Long, Integer> unlockPlan = getOrInitUnlockPlan(baseKey, activityId, allCards, userId);

        // 2. 获取抽卡次数 - 从已有抽奖记录计算
        Integer drawCount = userDraws.size();

        // 3. 分析已获得卡片 - 从已有抽奖记录计算
        Map<Long, Integer> obtainedCards = new HashMap<>();
        for (CatPrizeDraw draw : userDraws) {
            Long chancesId = draw.getChancesId();
            obtainedCards.put(chancesId, obtainedCards.getOrDefault(chancesId, 0) + 1);
        }

        // 检查是否已集齐所有卡片
        if (obtainedCards.size() == allCards.size() &&
                allCards.stream().allMatch(card -> obtainedCards.containsKey(card.getId()))) {
            log.info("用户已集齐所有卡片 [用户:{}, 活动:{}]", userId, activityId);
            return null; // 已集齐全部卡片
        }

        // 4. 获取或初始化概率分布 - 支持Redis过期重建
        Map<Long, BigDecimal> probabilities = getOrInitProbabilities(baseKey, allCards, userDraws, userId, activityId);

        // 5. 过滤当前可抽取的卡片（已达到解锁条件的卡片）
        List<CatChances> validCards = filterValidCards(allCards, unlockPlan, drawCount);

        if (validCards.isEmpty()) {
            log.warn("当前没有可抽取的卡片 [用户:{}, 活动:{}, 抽取次数:{}]", userId, activityId, drawCount);
            throw new RuntimeException("当前没有可抽取的卡片，请继续尝试");
        }

        // 记录有效卡片
        if (log.isDebugEnabled()) {
            log.debug("当前可抽取的卡片 [用户:{}, 活动:{}]: {}", userId, activityId,
                    validCards.stream().map(CatChances::getCardName).collect(Collectors.joining(", ")));
        }

        // 6. 执行抽奖
        CatChances drawnCard = weightedDraw(validCards, probabilities);
        log.info("抽中卡片: {} [用户:{}, 活动:{}]", drawnCard.getCardName(), userId, activityId);

        // 7. 更新概率分布
        updateProbabilities(probabilities, drawnCard.getId(), validCards);

        // 8. 存储更新后的概率数据到Redis
        try {
            redisTemplate.opsForValue().set(baseKey + PROBABILITY_SUFFIX,
                    JSONObject.toJSONString(probabilities),
                    EXPIRE_DAYS, TimeUnit.DAYS);
        } catch (Exception e) {
            log.error("保存更新后的概率分布到Redis失败，但不影响本次抽奖结果: {}", e.getMessage());
        }

        return drawnCard;
    }

    /**
     * 获取或初始化解锁计划
     */
    /**
     * 获取或初始化解锁计划 - 确保Redis过期后仍能正确重建
     */
    private Map<Long, Integer> getOrInitUnlockPlan(String baseKey, Long activityId,
                                                   List<CatChances> allCards, Long userId) {
        String unlockKey = baseKey + UNLOCK_PLAN_SUFFIX;
        String unlockPlanJson = redisTemplate.opsForValue().get(unlockKey);
        boolean isRedisCacheValid = false;

        // 尝试从Redis获取解锁计划
        if (unlockPlanJson != null) {
            try {
                Map<Long, Integer> unlockPlan = JSONObject.parseObject(unlockPlanJson, HashMap.class);

                // 验证解锁计划是否完整
                boolean needsReset = false;
                for (CatChances card : allCards) {
                    if (!unlockPlan.containsKey(card.getId())) {
                        needsReset = true;
                        log.warn("解锁计划不完整，缺少卡片ID:{}, 将重新生成解锁计划", card.getId());
                        break;
                    }
                }

                if (!needsReset) {
                    isRedisCacheValid = true;
                    log.debug("从Redis成功获取解锁计划 [用户:{}, 活动:{}]", userId, activityId);
                    return unlockPlan;
                }
            } catch (Exception e) {
                log.warn("解析解锁计划出错，将重新生成: {}", e.getMessage());
            }
        }

        // Redis缓存无效，重新生成解锁计划
        log.info("Redis解锁计划缓存{}，正在重新生成 [用户:{}, 活动:{}]",
                (unlockPlanJson == null ? "不存在" : "无效"), userId, activityId);

        // 生成新的解锁计划
        Map<Long, Integer> unlockPlan = generateUnlockPlan(allCards);

        // 记录日志
        if (log.isDebugEnabled()) {
            for (CatChances card : allCards) {
                log.debug("卡片[{}]: 解锁门槛={}", card.getCardName(),
                        unlockPlan.getOrDefault(card.getId(), 0));
            }
        }

        // 保存到Redis，即使Redis失败也不影响核心功能
        try {
            redisTemplate.opsForValue().set(unlockKey, JSONObject.toJSONString(unlockPlan),
                    EXPIRE_DAYS, TimeUnit.DAYS);
        } catch (Exception e) {
            log.error("保存解锁计划到Redis失败，但不影响本次抽奖: {}", e.getMessage());
        }

        return unlockPlan;
    }

    /**
     * 生成解锁计划
     * 使用每张卡片的quantity字段作为解锁门槛
     */
    private Map<Long, Integer> generateUnlockPlan(List<CatChances> allCards) {
        Map<Long, Integer> unlockPlan = new HashMap<>();

        // 直接使用quantity字段作为解锁条件
        for (CatChances card : allCards) {
            // 如果quantity为null或者小于0，则设置为0（立即可抽）
            Long unlockThreshold = card.getQuantity();
            unlockPlan.put(card.getId(), unlockThreshold != null && unlockThreshold > 0 ?
                    unlockThreshold.intValue() : 0);
        }

        return unlockPlan;
    }

    /**
     * 获取或初始化抽卡次数
     */
    private Integer getOrInitDrawCount(String baseKey) {
        String countKey = baseKey + DRAW_COUNT_SUFFIX;
        String countStr = redisTemplate.opsForValue().get(countKey);

        if (countStr != null) {
            return Integer.valueOf(countStr);
        }

        redisTemplate.opsForValue().set(countKey, "0", EXPIRE_DAYS, TimeUnit.DAYS);
        return 0;
    }

    /**
     * 获取或初始化已获得卡片
     */
    private Map<Long, Integer> getOrInitObtainedCards(String baseKey) {
        String obtainedKey = baseKey + OBTAINED_CARDS_SUFFIX;
        String obtainedJson = redisTemplate.opsForValue().get(obtainedKey);

        if (obtainedJson != null) {
            return JSONObject.parseObject(obtainedJson, HashMap.class);
        }

        Map<Long, Integer> obtained = new HashMap<>();
        redisTemplate.opsForValue().set(obtainedKey, JSONObject.toJSONString(obtained), EXPIRE_DAYS, TimeUnit.DAYS);
        return obtained;
    }

    /**
     * 获取或初始化概率分布 - 根据用户已有抽奖记录
     */
   /* private Map<Long, BigDecimal> getOrInitProbabilities(String baseKey, List<CatChances> allCards, List<CatPrizeDraw> userDraws) {
        String probKey = baseKey + PROBABILITY_SUFFIX;
        String probJson = redisTemplate.opsForValue().get(probKey);

        if (probJson != null) {
            try {
                Map<Long, BigDecimal> probabilities = JSONObject.parseObject(probJson, HashMap.class);

                // 验证概率数据是否完整
                boolean needsReset = false;
                for (CatChances card : allCards) {
                    if (!probabilities.containsKey(card.getId())) {
                        needsReset = true;
                        break;
                    }
                }

                if (!needsReset) {
                    return probabilities;
                }
            } catch (Exception e) {
                log.warn("Failed to parse probability data from Redis, will reinitialize: {}", e.getMessage());
            }
        }

        // 初始化概率分布
        Map<Long, BigDecimal> probabilities = new HashMap<>();

        // 使用数据库中配置的初始概率
        for (CatChances card : allCards) {
            probabilities.put(card.getId(), card.getChance());
        }

        // 根据已有抽奖记录调整概率
        Map<Long, Integer> drawCounts = new HashMap<>();
        for (CatPrizeDraw draw : userDraws) {
            Long chancesId = draw.getChancesId();
            drawCounts.put(chancesId, drawCounts.getOrDefault(chancesId, 0) + 1);
        }

        // 根据抽奖记录调整概率（每抽中一次，概率减半）
        for (Map.Entry<Long, Integer> entry : drawCounts.entrySet()) {
            Long cardId = entry.getKey();
            Integer count = entry.getValue();

            if (probabilities.containsKey(cardId)) {
                BigDecimal originalProb = probabilities.get(cardId);
                // 根据抽中次数计算新概率 (originalProb * 0.5^count)
                BigDecimal factor = BigDecimal.valueOf(Math.pow(0.5, count));
                probabilities.put(cardId, originalProb.multiply(factor));
            }
        }

        // 归一化概率
        normalizeProbabilities(probabilities);

        redisTemplate.opsForValue().set(probKey, JSONObject.toJSONString(probabilities), EXPIRE_DAYS, TimeUnit.DAYS);
        return probabilities;
    }*/

    /**
     * 获取或初始化概率分布 - 确保Redis过期后仍能基于历史记录正确计算
     */
    private Map<Long, BigDecimal> getOrInitProbabilities(String baseKey, List<CatChances> allCards,
                                                         List<CatPrizeDraw> userDraws, Long userId, Long activityId) {
        String probKey = baseKey + PROBABILITY_SUFFIX;
        String probJson = redisTemplate.opsForValue().get(probKey);
        boolean isRedisCacheValid = false;

        // 尝试从Redis获取概率分布
        if (probJson != null) {
            try {
                Map<Long, BigDecimal> probabilities = JSONObject.parseObject(probJson, HashMap.class);

                // 验证概率数据是否完整
                boolean needsReset = false;
                for (CatChances card : allCards) {
                    if (!probabilities.containsKey(card.getId())) {
                        needsReset = true;
                        log.warn("概率分布不完整，缺少卡片ID:{}, 将重新计算概率", card.getId());
                        break;
                    }
                }

                if (!needsReset) {
                    isRedisCacheValid = true;
                    log.debug("从Redis成功获取概率分布 [用户:{}, 活动:{}]", userId, activityId);
                    return probabilities;
                }
            } catch (Exception e) {
                log.warn("解析概率数据出错，将基于历史记录重新计算: {}", e.getMessage());
            }
        }

        // Redis缓存无效，基于历史记录重新计算概率
        log.info("Redis概率缓存{}，正在基于历史记录重建概率分布 [用户:{}, 活动:{}]",
                (probJson == null ? "不存在" : "无效"), userId, activityId);

        // 初始化概率分布 - 使用数据库中配置的初始概率
        Map<Long, BigDecimal> probabilities = new HashMap<>();
        for (CatChances card : allCards) {
            probabilities.put(card.getId(), card.getChance());
        }

        // 统计用户每张卡的抽中次数
        Map<Long, Integer> drawCounts = new HashMap<>();
        for (CatPrizeDraw draw : userDraws) {
            Long chancesId = draw.getChancesId();
            drawCounts.put(chancesId, drawCounts.getOrDefault(chancesId, 0) + 1);
        }

        // 根据抽奖记录调整概率（每抽中一次，概率减半）
        for (Map.Entry<Long, Integer> entry : drawCounts.entrySet()) {
            Long cardId = entry.getKey();
            Integer count = entry.getValue();

            if (probabilities.containsKey(cardId)) {
                BigDecimal originalProb = probabilities.get(cardId);
                // 根据抽中次数计算新概率 (originalProb * 0.5^count)
                BigDecimal factor = BigDecimal.valueOf(Math.pow(0.5, count));
                probabilities.put(cardId, originalProb.multiply(factor));
            }
        }

        // 归一化概率
        normalizeProbabilities(probabilities);

        // 记录日志，展示部分关键卡片的概率变化
        log.info("已重建概率分布 [用户:{}, 活动:{}], 卡片总数:{}, 已抽卡片数:{}",
                userId, activityId, allCards.size(), drawCounts.size());

        // 以更低的日志级别记录详细概率信息（仅用于调试）
        if (log.isDebugEnabled()) {
            for (CatChances card : allCards) {
                String status = drawCounts.containsKey(card.getId())
                        ? String.format("已抽%d次, 调整后概率:%.6f",
                        drawCounts.get(card.getId()), probabilities.get(card.getId()))
                        : String.format("未抽过, 原始概率:%.6f", probabilities.get(card.getId()));

                log.debug("卡片[{}]: {}", card.getCardName(), status);
            }
        }

        // 保存到Redis，即使Redis失败也不影响核心功能
        try {
            redisTemplate.opsForValue().set(probKey, JSONObject.toJSONString(probabilities),
                    EXPIRE_DAYS, TimeUnit.DAYS);
        } catch (Exception e) {
            log.error("保存概率分布到Redis失败，但不影响本次抽奖: {}", e.getMessage());
        }

        return probabilities;
    }

    /**
     * 归一化概率
     */
    private void normalizeProbabilities(Map<Long, BigDecimal> probabilities) {
        BigDecimal total = BigDecimal.ZERO;
        for (BigDecimal prob : probabilities.values()) {
            total = total.add(prob);
        }

        if (total.compareTo(BigDecimal.ZERO) > 0) {
            for (Map.Entry<Long, BigDecimal> entry : probabilities.entrySet()) {
                // 防止除数为0
                BigDecimal normalized = total.compareTo(BigDecimal.ZERO) > 0 ?
                        entry.getValue().divide(total, 6, BigDecimal.ROUND_HALF_UP) :
                        BigDecimal.valueOf(1.0 / probabilities.size());
                probabilities.put(entry.getKey(), normalized);
            }
        }
    }

    /**
     * 过滤出当前可抽取的卡片
     */
    private List<CatChances> filterValidCards(List<CatChances> allCards, Map<Long, Integer> unlockPlan, Integer drawCount) {
        return allCards.stream()
                .filter(card -> unlockPlan.getOrDefault(card.getId(), 0) <= drawCount)
                .collect(Collectors.toList());
    }

    /**
     * 加权随机抽取卡片
     */
    private CatChances weightedDraw(List<CatChances> validCards, Map<Long, BigDecimal> probabilities) {
        BigDecimal totalWeight = BigDecimal.ZERO;
        Map<CatChances, BigDecimal> cardWeights = new HashMap<>();

        for (CatChances card : validCards) {
            BigDecimal weight = probabilities.getOrDefault(card.getId(), BigDecimal.ZERO);
            cardWeights.put(card, weight);
            totalWeight = totalWeight.add(weight);
        }

        if (totalWeight.compareTo(BigDecimal.ZERO) <= 0) {
            // 如果总权重为0，则均等概率
            return validCards.get(new Random().nextInt(validCards.size()));
        }

        BigDecimal random = BigDecimal.valueOf(Math.random()).multiply(totalWeight);
        BigDecimal cumulative = BigDecimal.ZERO;

        for (Map.Entry<CatChances, BigDecimal> entry : cardWeights.entrySet()) {
            cumulative = cumulative.add(entry.getValue());
            if (random.compareTo(cumulative) <= 0) {
                return entry.getKey();
            }
        }

        // 兜底，返回最后一个卡片
        return validCards.get(validCards.size() - 1);
    }

    /**
     * 更新概率分布
     */
    private void updateProbabilities(Map<Long, BigDecimal> probabilities, Long drawnCardId, List<CatChances> validCards) {
        // 已抽中卡片概率减半
        BigDecimal currentProb = probabilities.get(drawnCardId);
        probabilities.put(drawnCardId, currentProb.multiply(BigDecimal.valueOf(0.5)));

        // 归一化所有概率
        BigDecimal total = BigDecimal.ZERO;
        for (CatChances card : validCards) {
            total = total.add(probabilities.get(card.getId()));
        }

        for (CatChances card : validCards) {
            Long cardId = card.getId();
            // 防止除数为0
            BigDecimal normalized = total.compareTo(BigDecimal.ZERO) > 0 ?
                    probabilities.get(cardId).divide(total, 6, BigDecimal.ROUND_HALF_UP) :
                    BigDecimal.valueOf(1.0 / validCards.size());
            probabilities.put(cardId, normalized);
        }
    }

    /**
     * 保存数据到Redis
     */
    private void saveDataToRedis(String baseKey, Map<Long, Integer> unlockPlan, Integer drawCount,
                                 Map<Long, Integer> obtainedCards, Map<Long, BigDecimal> probabilities) {
        redisTemplate.opsForValue().set(baseKey + DRAW_COUNT_SUFFIX, drawCount.toString(), EXPIRE_DAYS, TimeUnit.DAYS);
        redisTemplate.opsForValue().set(baseKey + OBTAINED_CARDS_SUFFIX, JSONObject.toJSONString(obtainedCards), EXPIRE_DAYS, TimeUnit.DAYS);
        redisTemplate.opsForValue().set(baseKey + PROBABILITY_SUFFIX, JSONObject.toJSONString(probabilities), EXPIRE_DAYS, TimeUnit.DAYS);
    }
}